/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package client;

import com.sun.org.apache.bcel.internal.generic.AALOAD;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.LinkedList;

/**
 *
 * @author Andi
 */
public class Client {
    private Socket clientSock;
    private DataInputStream din;
    private DataOutputStream dout;
    private int port = 10000;
    private String hostname = "localhost";
    private String username;
    private String password;
    private String rootPath;
    private LinkedList<FileProp> listFileServer;
    private LinkedList<FileProp> listFileClient;
    private long lastLogIn;
    private long lastSyn;
    private String listFileInString;
    private boolean isLogin;

    public Client(){
        this.listFileServer = new LinkedList<FileProp>();
        this.listFileClient = new LinkedList<FileProp>();
    }

    public void setUsername(String usrname){
        this.username = usrname;
    }

    public String getUsername(){
        return this.username;
    }

    public void setPassword(String pass){
        this.password = pass;
    }

    public String getPassword(){
        return this.password;
    }

    public void setRootPath(String p){
        this.rootPath = p;
    }

    public String getRootPath(){
        return this.rootPath;
    }

    public void setLastLogIn(long l){
        this.lastLogIn = l;
    }

    public long getLastLogIn(){
        return this.lastLogIn;
    }

    public void setLastSyn(long s){
        this.lastSyn = s;
    }

    public long getLastSyn(){
        return this.lastSyn;
    }

    public void setListFileClient(LinkedList<FileProp> l){
        this.listFileClient = l;
    }

    public LinkedList<FileProp> getListFileClient(){
        return this.listFileClient;
    }

    public void setListFileServer(LinkedList<FileProp> l){
        this.listFileServer = l;
    }

    public LinkedList<FileProp> getListFileServer(){
        return this.listFileServer;
    }

    public void setListFileInString(String s){
        this.listFileInString = s;
    }

    public String getListFileInString(){
        return this.listFileInString;
    }

    public void setIsLogin(boolean b){
        this.isLogin = b;
    }

    public boolean getIsLogin(){
        return this.isLogin;
    }

    public boolean startClient() throws UnknownHostException, IOException{
        boolean result = false;
        System.out.println("Membuat koneksi ke " + hostname + ":" + port + "...");
        clientSock = new Socket(hostname, port);
        dout = new DataOutputStream(clientSock.getOutputStream());
        din = new DataInputStream(clientSock.getInputStream());
        System.out.println("Berhasil membuat koneksi ke " + hostname + ":" + port + "...");
        String pstr = "DOBOG protocol";
        dout.writeUTF(pstr);
        dout.flush();
        String response = din.readUTF();
        if (response.equalsIgnoreCase(pstr)){
            // handshake sukses
            result = true;
        }
        else{
            result = false;
        }
        System.out.println("Message response handshake yang diterima = " + response);
        return result;
    }

    public boolean login(String username, String password) throws IOException{
        dout = new DataOutputStream(clientSock.getOutputStream());
        din = new DataInputStream(clientSock.getInputStream());
        String autentikasi = username + "|" + password;
        dout.writeUTF(autentikasi);
        dout.flush();
        String response = din.readUTF();
        // login sukses
        return (response.equalsIgnoreCase("ACK"));
    }

    public boolean logout(String username, String password) throws IOException{
        dout = new DataOutputStream(clientSock.getOutputStream());
        din = new DataInputStream(clientSock.getInputStream());
        String autentikasi = "logout";
        dout.writeUTF(autentikasi);
        dout.flush();
        String response = din.readUTF();
        // login sukses
        boolean result = false;
        if (response.equalsIgnoreCase("logout")){
            result = true;
            clientSock.close();
        }
        return (result);
    }

    public String getPathFromRoot(String p){
        String path = "";
        int pathLength = rootPath.length();

        if (!p.equalsIgnoreCase(rootPath)){
            if (p.substring(0, pathLength).equalsIgnoreCase(rootPath)){
                path += p.substring(pathLength+1);
            }
        }

        return path;
    }

    public String getCurrentPath(String p){
        String s = "", s2 = "";
        if (!p.equals("")){
            s += rootPath + "/" + p;
            s2 = this.changePathName(s);
        }
        else{
            s2 = rootPath;
        }
        return s2;
    }

    public String changePathName(String p){
        String path = "";
        path = p.replace('\\', '/');
        return path;
    }

    public void getListOfFileClientFromFolder(String p){
        File folder = new File(p);
        File[] listOfFile = folder.listFiles();
        if (listOfFile.length != 0){
            String pt = this.getPathFromRoot(p);
            for (int i = 0; i < listOfFile.length; ++i){
                if (listOfFile[i].isFile()){
                    FileProp a = new FileProp(listOfFile[i].getName(), pt, listOfFile[i].lastModified());
//                    a.setFileName(listOfFile[i].getName());
//                    a.setFilePath(pt);
//                    a.setLastMod(listOfFile[i].lastModified());
                    this.listFileClient.addLast(a);
                }
                else if (listOfFile[i].isDirectory()){
                    String path = p + "/" + listOfFile[i].getName();
                    getListOfFileClientFromFolder(path);
                }
                else{

                }
            }
        }
    }

    public void getListOfFileServerFromString(String listnya){
        //TODO : sesuaikan dengan adanya folder
        if (!listnya.equals("")){
            String[] satufile = listnya.split("\n");
            for (int i = 0; i < satufile.length; ++i){
                String[] prop = satufile[i].split("\\|");
                FileProp a = new FileProp();
                String name = prop[0];
//                String p = prop[1];
//                long last = Long.parseLong(prop[2]);
                long last = Long.parseLong(prop[1]);
                a.setFileName(name);
//                a.setFilePath(p);
                a.setLastMod(last);
                listFileServer.add(a);
            }
        }
        else{
            
        }
    }

    //masih belum selesai
    public void compareListFileClientServer() throws UnknownHostException, IOException, Exception{
        //TODO : sesuaikan dengan adanya folder
        for (int i = 0; i < listFileServer.size(); ++i){
            boolean ada = false;
            for (int j = 0; j < listFileClient.size(); ++j){
//                if (listFileServer.get(i).getFileName().equalsIgnoreCase(listFileClient.get(j).getFileName()) &&
//                        listFileServer.get(i).getFilePath().equalsIgnoreCase(listFileClient.get(j).getFilePath())){
                if (listFileServer.get(i).getFileName().equalsIgnoreCase(listFileClient.get(j).getFileName())){

                    ada  = true;
                    if (listFileServer.get(i).getLastMod() < listFileClient.get(j).getLastMod()){
                        //request untuk update yang di server / unggah file
                        this.requestUploadFile(listFileClient.get(j).getFileName(), listFileClient.get(j).getFilePath(), listFileClient.get(j).getLastMod());
                    }
                    else if (listFileServer.get(i).getLastMod() > listFileClient.get(j).getLastMod()){
                        //request untuk update yang di client / unduh file
                        this.requestDownloadFile(listFileServer.get(i).getFileName(), listFileServer.get(i).getFilePath(), listFileServer.get(i).getLastMod());
                    }
                    else if (listFileServer.get(i).getLastMod() == listFileClient.get(j).getLastMod()){
                        //tidak terjadi apa2
                    }
                }
            }

            if (!ada){
                if (listFileServer.get(i).getLastMod() < this.lastSyn){
                    //request untuk delete file
                    this.requestDeleteFile(listFileServer.get(i).getFileName(), listFileServer.get(i).getFilePath());
                }
                else if (listFileServer.get(i).getLastMod() > this.lastSyn){
                    //request untuk unduh
                    this.requestDownloadFile(listFileServer.get(i).getFileName(), listFileServer.get(i).getFilePath(), listFileServer.get(i).getLastMod());
                }
                else{
                    //request untuk unduh
                    this.requestDownloadFile(listFileServer.get(i).getFileName(), listFileServer.get(i).getFilePath(), listFileServer.get(i).getLastMod());
                }
            }
        }

        for (int k = 0; k < listFileClient.size(); ++k){
            boolean ada2 = false;
            for (int l = 0; l < listFileServer.size(); ++l){
//                if (listFileClient.get(k).getFileName().equalsIgnoreCase(listFileServer.get(l).getFileName()) &&
//                        listFileClient.get(k).getFilePath().equalsIgnoreCase(listFileServer.get(l).getFilePath())){
                if (listFileClient.get(k).getFileName().equalsIgnoreCase(listFileServer.get(l).getFileName())){

                    ada2 = true;
                }
            }

            if (!ada2){
                //request untuk unggah
                this.requestUploadFile(listFileClient.get(k).getFileName(), listFileClient.get(k).getFilePath(), listFileClient.get(k).getLastMod());
            }
        }

        this.lastSyn = System.currentTimeMillis();
    }

    public boolean requestFileList() throws UnknownHostException, IOException{
        boolean result = false;
        System.out.println("meminta filelist ke " + hostname + ":" + port + "...");
        //clientSock = new Socket(hostname, port);
        //dout = new DataOutputStream(clientSock.getOutputStream());
        //din = new DataInputStream(clientSock.getInputStream());
        System.out.println("masih meminta filelist ke " + hostname + ":" + port + "...");
        String pstr = "requestListFile|"+username;
        dout.writeUTF(pstr);
        dout.flush();
        String response = din.readUTF();
        System.out.println("respons pas request filelist = "+response + "...");
        // pengiriman request selesai
        if (!response.equals("")){
            result = true;
        }
        this.listFileInString = response;
        this.getListOfFileServerFromString(this.listFileInString);
        System.out.println("Message response request file list yang diterima = " + response);
        return result;
    }

    //masih belum selesai
    public void requestDownloadFile(String fname, String fpath, long flast) throws UnknownHostException, IOException, Exception{
        System.out.println("meminta file "+ fname +" di " + fpath +" ke " + hostname + ":" + port + "...");
        System.out.println("masih meminta file "+ fname +" di " + fpath +" ke " + hostname + ":" + port + "...");
        String pstr = "requestDownloadFile|"+ username + "|"+ fname + "|" + fpath;
        dout.writeUTF(pstr);
        dout.flush();

        this.downloadFile(fname, fpath, flast);
        System.out.println("Mengunduh file " + fname + " di " + fpath);
    }

    //masih belum selesai
    public void requestDeleteFile(String fname, String fpath) throws UnknownHostException, IOException{
        System.out.println("meminta delete file "+ fname +" di " + fpath +" ke " + hostname + ":" + port + "...");
        System.out.println("masih meminta delete file "+ fname +" di " + fpath +" ke " + hostname + ":" + port + "...");
        String pstr = "requestDeleteFile|"+ username + "|"+ fname + "|" + fpath;
        dout.writeUTF(pstr);
        dout.flush();
        String response = din.readUTF();
        System.out.println("respons setelah request delete = "+ response + "...");
        // pengiriman request selesai
        if (response.equalsIgnoreCase("deleteFinished")){
            System.out.println("Permintaan delete dikabulkan ");
        }
        else{
            System.out.println("permintaan delete tidak terkabul ");
        }
    }

    public void requestUploadFile(String fname, String fpath, long flast) throws UnknownHostException, IOException, Exception{
        System.out.println("meminta upload file "+ fname +" di " + fpath +" ke " + hostname + ":" + port + "...");
        System.out.println("masih meminta upload file "+ fname +" di " + fpath +" ke " + hostname + ":" + port + "...");
        //String pstr = "requestUploadFile|"+ username + "|"+ fname + "|" + flast;
        String pstr = "requestUploadFile|"+ username + "|"+ fname + "|" + flast + "|" + fpath;
        dout.writeUTF(pstr);
        dout.flush();
        String response = din.readUTF();
        System.out.println("respon setelah request upload = "+response + "...");
        // pengiriman request selesai
        if (response.equalsIgnoreCase("uploadStart")){
            uploadFile(fname, fpath, flast);
            String response2 = din.readUTF();
            if (response2.equalsIgnoreCase("OK")){
                System.out.println("upload file berhasil ");
            }
            else{
                System.out.println("upload file gagal ");
            }
        }
        else{
            System.out.println("Gagal mengirim request untuk upload ");
        }
    }

    public void uploadFile(String fname, String fpath, long flast) throws Exception{
        String lokasi = this.getCurrentPath(fpath) + "\\" + fname;
        File file = new File(lokasi);
        file.setLastModified(flast);
        this.sendFile(file);
    }

    public void downloadFile(String fname, String fpath, long flast) throws Exception{
        String lokasi = this.getCurrentPath(fpath) + "\\" + fname;
        File file = new File(lokasi);
        file.setLastModified(flast);
        this.receiveFile(file);
        dout.writeUTF("OK");
    }

    public void sendFile(File f) throws Exception {
        try {
            dout.writeLong(f.length());
            FileInputStream fin = new FileInputStream(f);
            byte[] _byte = new byte[1024];
            int length;
            do {
                length = fin.read(_byte);
                if (length != -1) {
                    dout.write(_byte, 0, length);
                }
            } while (length != -1);
            fin.close();
            String diterima = din.readUTF();
            if (diterima.startsWith("OK")) {
                //berhasil
            } else {
                //gagal
                System.out.println(diterima);
                throw new Exception("gagal mengirim");
            }
        } catch (Exception e) {
            if (!e.getMessage().equals("file sudah ada")) {
                throw e;
            }
        }
    }

    public void receiveFile(File f) throws Exception {
        long size = din.readLong();
        FileOutputStream fout = new FileOutputStream(f);
        int length;
        byte[] _byte = new byte[1024];
        long byteread = 0;
        while (byteread < size) {
            length = din.read(_byte);
            if (length != -1) {
                fout.write(_byte, 0, length);
                byteread += length;
            }
        }
        fout.close();
        //kirim OK
        dout.writeUTF("OK\n");
    }

    public void receiveFile2(File f, int fsize) throws Exception {
        int bytesRead;
        int current = 0;
        byte[] mybytearray = new byte[fsize];
        InputStream is = clientSock.getInputStream();
        FileOutputStream fos = new FileOutputStream(f);
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        bytesRead = is.read(mybytearray, 0, mybytearray.length);
        current = bytesRead;

        do{
            bytesRead = is.read(mybytearray, current, (mybytearray.length - current));
            if (bytesRead >= 0){
                current += bytesRead;
            }
        } while (bytesRead > -1);

        bos.write(mybytearray, 0, current);
        bos.flush();
        bos.close();
    }

    public void sendFile2(File f) throws Exception {
        byte[] mybytearray = new byte[(int)f.length()];
        FileInputStream fis = new FileInputStream(f);
        BufferedInputStream bis = new BufferedInputStream(fis);
        bis.read(mybytearray, 0, mybytearray.length);
        OutputStream os = clientSock.getOutputStream();
        os.write(mybytearray, 0, mybytearray.length);
        os.flush();
    }

    public void run() throws UnknownHostException, IOException, Exception{
        while(true){
            if (isLogin){
                this.getListOfFileClientFromFolder(this.rootPath);
                this.requestFileList();
                this.compareListFileClientServer();
            }
        }
    }

    public void writeLastSync() throws IOException{
        long l = this.getLastSyn();
        String s = Long.toString(l);
        FileWriter fw = new FileWriter("last_syn.txt");
        BufferedWriter out = new BufferedWriter(fw);
        out.write(s);
        out.close();
    }

    public void readLastSync() throws FileNotFoundException, IOException{
        File f = new File("last_syn.txt");
        FileReader fr = new FileReader(f);
        BufferedReader br = new BufferedReader(fr);
        StringBuffer sb = new StringBuffer();
        String s = br.readLine();
        long last = 0;

        if (s != null){
            sb.append(s);
            String str = sb.toString();
            last = Long.parseLong(str);
        }

        //return last;
        this.setLastSyn(last);
    }

    public void writePathToFile() throws IOException{
        String s = this.rootPath;
        FileWriter fw = new FileWriter("mypath.txt");
        BufferedWriter out = new BufferedWriter(fw);
        out.write(s);
        out.close();
    }

    public void readPathFromFile() throws IOException{
        File f = new File("mypath.txt");
        FileReader fr = new FileReader(f);
        BufferedReader br = new BufferedReader(fr);
        StringBuffer sb = new StringBuffer();
        String s = br.readLine();
        String str = "";

        if (s != null){
            sb.append(s);
            str = sb.toString();
        }
        str = this.changePathName(str);
        this.setRootPath(str);
    }

//    public static void main(String args[]) throws UnknownHostException, IOException{
//        Client client = new Client();
//        boolean result = client.startClient();
//        boolean ya = client.login("hyouda", "hyouda");
//        client.setRootPath("D:/tesgan");
//
//        String inimisal = "";
//
//        client.getListOfFileClientFromFolder("D:/tesgan/aduh");
//        System.out.println(client.getListFileClient().size());
//        for (int i = 0; i < client.getListFileClient().size(); ++i){
//            inimisal += client.getListFileClient().get(i).convertToStringInfoFile();
//            System.out.println(client.getListFileClient().get(i).convertToStringInfoFile());
//        }
//
//        client.getListOfFileServerFromString(inimisal);
//        System.out.println(client.getListFileServer().size());
//        for (int j = 0; j < client.getListFileServer().size(); ++j){
//            System.out.println(client.getListFileServer().get(j).convertToStringInfoFile());
//        }
//
//    }


}
